

package com.bolt.support.desensitization.autoconfigure;

import com.bolt.common.utils.StrUtil;
import com.bolt.convention.data.ResultMessage;
import com.bolt.support.desensitization.annotation.EnableDesensitization;
import com.bolt.support.desensitization.resolver.ResponseEntityTypeResolver;
import com.bolt.support.desensitization.resolver.ResultMessageTypeResolver;
import com.bolt.support.desensitization.resolver.TypeResolver;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;

import java.lang.reflect.AnnotatedParameterizedType;
import java.util.Map;
import java.util.Optional;


public class DesensitizationAutoConfiguration extends ApplicationObjectSupport {

    private static final String DESENSITIZATION_ADVISOR = "desensitizationAdvisor";


    @Bean
    @ConditionalOnMissingBean(name = DESENSITIZATION_ADVISOR)
    public Advisor desensitizationAdvisor() {
        AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
        advisor.setAdvice(new MethodDesensitizationInterceptor());
        advisor.setExpression(pointcutExpression());
        return advisor;
    }

    @Bean
    public TypeResolver<ResponseEntity<Object>, AnnotatedParameterizedType> responseEntityResolver() {
        return new ResponseEntityTypeResolver();
    }

    @Bean
    public TypeResolver<ResultMessage<Object>, AnnotatedParameterizedType> resultMessageTypeResolver() {
        return new ResultMessageTypeResolver();
    }


    public String findBootClassPackage() {
        ApplicationContext context = getApplicationContext();
        Map<String, Object> annotatedBeans = context.getBeansWithAnnotation(SpringBootApplication.class);
        Assert.isTrue(!annotatedBeans.isEmpty(), () -> "无法获取SpringApplication！当前应用可能不是spring-boot工程。");
        Class targetClass = annotatedBeans.values().toArray()[0].getClass();
        return targetClass.getPackage().getName();
    }

    /**
     * @return 切点表达式字符串
     */
    private String pointcutExpression() {
        ApplicationContext context = getApplicationContext();
        String pointcutExpression = null;
        String[] enableDesensitizationBeanNames = context.getBeanNamesForAnnotation(EnableDesensitization.class);
        if (enableDesensitizationBeanNames.length != 0) {
            for (String enableDesensitizationBeanName : enableDesensitizationBeanNames) {
                Object enableWebSocketBean = context.getBean(enableDesensitizationBeanName);
                EnableDesensitization enableDesensitization = AnnotationUtils.findAnnotation(enableWebSocketBean.getClass(), EnableDesensitization.class);
                assert enableDesensitization != null;
                if (StrUtil.isNotBlank(enableDesensitization.pointcutExpression())) {
                    pointcutExpression = enableDesensitization.pointcutExpression();
                    break;
                }
            }
        }

        return Optional.ofNullable(pointcutExpression)
                .orElseGet(()->"execution(* "
                        + findBootClassPackage()
                        + "..*.*(..))");
    }

}
